package com.dcstudy.redis.service;

import com.dcstudy.redis.entities.User;
import com.dcstudy.redis.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author lichenyang
 * @create 2021-05-18 10:15 上午
 */
@Service
@Slf4j
public class UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisTemplate<String, User> redisTemplate;

    public static final String CACHE_KEY_USER = "user:";

    /**
     * 添加数据3步
     * 1. 将数据插入到mysql中
     * 2. 查询数据库,判断mysql是否插入成功
     * 3. 将查询内容存入到redis中, 不需要判断查出来的数据是否为null
     *
     * @param user
     */
    @PostMapping("/add")
    public void add(@RequestBody User user) {
        int i = userMapper.insertSelective(user);
        if (i > 0) {
            // 数据库中的数据确实存在以后
            User dbUser = userMapper.selectByPrimaryKey(user.getId());
            String key = CACHE_KEY_USER + dbUser.getId();
            redisTemplate.opsForValue().set(key, dbUser);
        }
    }

    @PostMapping("delete")
    public void delete(Integer id) {
        int i = userMapper.deleteByPrimaryKey(id);
        if (i > 0) {
            String key = CACHE_KEY_USER + id;
            redisTemplate.delete(key);
        }
    }

    /**
     * 1. 修改mysql中的数据
     * 2. 查询数据库,判断mysql是否插入成功
     * 3. 将查询内容存入到redis中, 不需要判断查出来的数据是否为null
     *
     * @param user
     */
    @PostMapping("update")
    public void update(@RequestBody User user) {
        int i = userMapper.updateByPrimaryKey(user);
        if (i > 0) {
            User dbUser = userMapper.selectByPrimaryKey(user.getId());
            String key = CACHE_KEY_USER + dbUser.getId();
            redisTemplate.opsForValue().set(key, dbUser);
        }
    }

    /**
     * 查询步骤:
     * 1. 查询redis, 判断redis中是否存在数据
     * 1.1. 如果redis中有 直接返回
     * 1.2. 如果redis中没有
     *
     * 2. 查询数据库
     * 2.1 如果数据库中有: 将数据库中的数据存入到redis中 然后返回
     * 2.2 如果数据库中没有: 直接返回
     *
     * @param id
     * @return
     */
    @GetMapping("select")
    public User select(Integer id) {
        String key = CACHE_KEY_USER + id;
        User user = redisTemplate.opsForValue().get(key);
        if(Objects.isNull(user)){
            // 对于搞QPS 的优化, 进来先加锁, 保证一个请求操作, 让外面的redis 等待一下, 避免击穿mysql
            synchronized (UserService.class){
                user = redisTemplate.opsForValue().get(key);
                if(Objects.isNull(user)){
                    user = userMapper.selectByPrimaryKey(id);
                    if (Objects.isNull(user)) {
                        return user;
                    } else {
                        // 为什么使用 setIfAbsent 而不是 set ?
                        // 因为执行update操作的时候, 已经修改过一次redis了 如果此时使用 set 操作, 也就只会出现 2 中情况
                        // 1. update操作前执行set, 未变动的数据重新插入一次数据库
                        // 2. update操作后执行set, update操作的时候已经执行过一个set操作了, 此时还是将redis中的数据重新入redis一次
                        redisTemplate.opsForValue().setIfAbsent(key, user, 7L, TimeUnit.DAYS);
                    }
                }
            }
        }
        return user;
    }
}